import unittest
import os
import sys
import os.path
import datetime
import shutil

sys.path.append(os.path.pardir)

from web_stats_class import *

class BuildWebLogDbTest(unittest.TestCase):
    
    def setUp(self):
        self.path_to_test_directory = os.path.realpath(os.path.curdir) + os.path.sep
        full_log_file_path =  self.path_to_test_directory + "simple.log.sample"
        self.db_file = os.path.realpath(os.path.curdir) + os.path.sep + "web_stats_log_test.db3"
        self.db_connection_string = "sqlite:///" + self.db_file
        
        positions_in_log = {"request_date_time" : 0, "client_ip" : 3, "request_url" : 2, "http_response_code" : 6, "size_bytes" : 4, "request_response_time_secs" : 5}
        self.configuration = {"log_file_path": full_log_file_path, "db_connection_string" : self.db_connection_string, "delimiter" : "|", 
                                  "positions_in_log" : positions_in_log}

        try:
            if os.path.isfile(self.db_file):
                os.remove(self.db_file)
        except WindowsError:
            pass
            
    def test_init(self):
        """Test initializing the BuildWebLogDb Class"""
        BuildWebLogDb(self.configuration)
        self.assertTrue(os.path.isfile(self.db_file))
        
    def test_build(self):
        """Test reading a | delimited web log file"""
        web_log = BuildWebLogDb(self.configuration)
        web_log.update()
        web_log.close()
        web_log = None
        shutil.copyfile(self.db_file,self.path_to_test_directory + "simple_log.db3") # Keep a copy of the database
        
    def test_read_common(self):
        """Read Apache Common Format with Spaces"""
        full_log_file_path = self.path_to_test_directory + "common.log.sample"
        positions_in_log = {"request_date_time" : 3, "client_ip" : 0, "request_url" : 4, "http_response_code" : 5, "size_bytes" : 6}
        configuration = {"log_file_path": full_log_file_path,"db_connection_string": self.db_connection_string, "delimiter" : " ", 
                                  "positions_in_log" : positions_in_log}
        web_log = BuildWebLogDb(configuration)
        web_log.update()
        self.assertEquals(web_log.count(),200)
        web_log.close()
        web_log = None

        shutil.copyfile(self.db_file,self.path_to_test_directory + "common_log.db3")

    def test_write_delimiter(self):
        full_log_file_path = self.path_to_test_directory + "common.log.sample"
        positions_in_log = {"request_date_time" : 3, "client_ip" : 0, "request_url" : 4, "http_response_code" : 5, "size_bytes" : 6}
        configuration = {"log_file_path": full_log_file_path,"db_connection_string": self.db_connection_string, "delimiter" : " ",
                                  "positions_in_log" : positions_in_log}
        web_log = BuildWebLogDb(configuration)

        delimited_file_name = 'common.log.sample.txt'
        web_log.write_delimited(delimited_file_name)

        f = open(delimited_file_name,'r')
        delimited_logs = []
        for line in f:
            delimited_logs.append(line.split('\t'))

        self.assertEquals(len(delimited_logs[0]),len(delimited_logs[1]))

    
    def test_read_common_with_rotated_log(self):
        full_log_file_path = self.path_to_test_directory + "common.log.sample"
        full_log_rotated_file_path = self.path_to_test_directory + "common.log.1.sample"
        
        positions_in_log = {"request_date_time" : 3, "client_ip" : 0, "request_url" : 4, "http_response_code" : 5, "size_bytes" : 6}
        configuration = {"log_file_path": full_log_file_path,"db_connection_string": self.db_connection_string, "delimiter" : " ", 
                                  "positions_in_log" : positions_in_log}
        
        configuration["log_rotated_file_path"] = full_log_rotated_file_path
        
        web_log = BuildWebLogDb(configuration)
        web_log.update()
        
        self.assertEquals(web_log.count(),220)
        web_log.close()
        web_log = None
        shutil.copyfile(self.db_file,self.path_to_test_directory + "common_log_rotated.db3")
        
    def test_is_log_rotated(self):
        """Test log rotation handling. In case the script is run regularly we need to take care of the case that 
            a log is rotated before we read the new log. This will leave a gap in the imported log files. 
        """
        
        full_log_file_path = self.path_to_test_directory + "common.log.sample"
        full_log_rotated_file_path = self.path_to_test_directory + "common.log.1.sample"
        
        
        positions_in_log = {"request_date_time" : 3, "client_ip" : 0, "request_url" : 4, "http_response_code" : 5, "size_bytes" : 6}
        configuration = {"log_file_path": full_log_file_path,"db_connection_string": self.db_connection_string, "delimiter" : " ", 
                                  "positions_in_log" : positions_in_log }
        
        # Case 1: We have not defined a rotated log file
        web_log_case1 = BuildWebLogDb(configuration)
        self.assertFalse(web_log_case1.is_log_rotated())
        
        # Case 2: We defined a rotated log file but it does not exist yet because the log file has not been rotated
        configuration["log_rotated_file_path"] = full_log_rotated_file_path + ".test"
        web_log_case2 = BuildWebLogDb(configuration)
        self.assertFalse(web_log_case2.is_log_rotated())
        
        # Case 3: The rotated log file exists but it has not been rotated since last update
        configuration["log_rotated_uid"] = str(2216L)
        configuration["log_rotated_file_path"] = full_log_rotated_file_path
        web_log_case3 = BuildWebLogDb(configuration)
        self.assertFalse(web_log_case2.is_log_rotated())
        
        # Case 4: The rotated log file exists and has been rotated so we need to read the rotated log first
        configuration["log_rotated_uid"] = str(22414L)
        web_log_case4 = BuildWebLogDb(configuration)
        self.assertTrue(web_log_case4.is_log_rotated())

        web_log_case1.close()
        web_log_case1 = None
        web_log_case2.close()
        web_log_case2 = None
        web_log_case3.close()
        web_log_case3=None
        web_log_case4.close()
        web_log_case4 = None

    
    def test_read_combined(self):
        """Read Apache Combined Format with Spaces"""
        full_log_file_path = self.path_to_test_directory + "combined.log.sample"
        positions_in_log = {"request_date_time" : 3, "client_ip" : 0, "request_url" : 4, "http_response_code" : 5, "size_bytes" : 6,
                            "referer" : 7, "user_agent": 8}
        configuration = {"log_file_path": full_log_file_path,"db_connection_string": self.db_connection_string, "delimiter" : " ", 
                                  "positions_in_log" : positions_in_log}
        web_log = BuildWebLogDb(configuration)
        web_log.update()
        web_log.close()
        web_log = None
        shutil.copyfile(self.db_file,self.path_to_test_directory + "combined_log.db3")
        
    def tearDown(self):
        pass
    
class TestUtilityFunctions(unittest.TestCase):
    
    def test_extract_date(self):
        extracted_date = extract_date("[16/Aug/2007:04:36:07 +0000]")
        self.assertEquals(extracted_date,datetime.datetime(2007,8,16,4,36,7))
        
    def test_join_escaped_space_split_string(self):
        log_line = '127.0.0.1 - - [02/Nov/2007:13:45:15 -0400] "GET /images/curbase_common/students_photos/99613.jpg HTTP/1.1" 200 8021'
        split_log = log_line.split(" ")
        rejoined_split_string = join_escaped_space_split_string(split_log,"[","]")
        self.assertEquals(rejoined_split_string[3], '[02/Nov/2007:13:45:15 -0400]')
        rerejoined_split_string = join_escaped_space_split_string(rejoined_split_string,'"')
        self.assertEquals(rerejoined_split_string[4], '"GET /images/curbase_common/students_photos/99613.jpg HTTP/1.1"')
        self.assertEquals(len(rerejoined_split_string),7)
        
        log_line_more_complex = '127.0.0.1 - - [07/Nov/2007:07:07:20 -0500] "GET /images/news/ExacTracXRay6D_tn.jpg HTTP/1.1" 304 - "http://www.example.org/" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"'
        self.assertEquals(join_escaped_space_split_string(join_escaped_space_split_string(log_line_more_complex.split(" "),"[","]"),'"')[7],'"http://www.example.org/"')
        
        log_line_even_more_complex = '127.0.0.1 - - [27/Jul/2007:16:06:46 -0400] "GET /guestbook/https://www.example.net/guestbook/mailto:eyesvicar@aol.com                                            HTTP/1.1" 403 275'
        self.assertEquals(join_escaped_space_split_string(join_escaped_space_split_string(log_line_even_more_complex.split(),"[","]"),'"')[4],'"GET /guestbook/https://www.example.net/guestbook/mailto:eyesvicar@aol.com HTTP/1.1"')
        
    def test_extract_url_from_request(self):
        request_line = "POST /students/students/menu_next.cfm HTTP/1.1"
        url_string = extract_url_from_request(request_line)
        self.assertEquals(url_string, "/students/students/menu_next.cfm")

    def test_extract_url_from_blank_request(self):
        request_line = ""
        url_string = extract_url_from_request(request_line)
        self.assertEquals(url_string, "")

    
    def test_split_request(self):
        log_line1 = '127.0.0.1 - - [02/Nov/2007:13:45:15 -0400] "GET /images/curbase_common/students_photos/99613.jpg HTTP/1.1" 200 8021'
        self.assertEquals(len(split_request(log_line1, " ")),7)
        
        log_line2 = '127.0.0.1 - - [21/Oct/2007:18:50:31 -0400] "GET /body.cfm?id=119 HTTP/1.0" 200 64403 "-" "Mozilla/5.0 (compatible; ANTGalio/2.3.21.00.devel [Oct 17 2007 01:08:47]; Linux2.6.9-1.667/i686)"'
        self.assertEquals(len(split_request(log_line2, " ")),9)
        
        log_line3 = '192.168.11.4 - - [29/Oct/2007:02:29:02 -0400] "GET /body.cfm?id=103 HTTP/1.1                                      " 200 75003 "http://example.org" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"'
        self.assertEquals(len(split_request(log_line3, " ")),9)

if __name__ == "__main__":
    unittest.main()